package game.breeze.data.c;

import game.breeze.ani.Animation;
import game.breeze.astar.AStar;
import game.breeze.astar.AStarNode;
import game.breeze.constants.ActionType;
import game.breeze.constants.EventType;
import game.breeze.constants.MapItemType;
import game.breeze.math.Position;
import game.breeze.resource.GameAction;
import game.breeze.resource.GameEvent;
import game.breeze.resource.action.MoveAction;
import game.breeze.resource.action.ParallelAction;
import game.breeze.resource.action.RotateAction;
import game.breeze.resource.action.SequenceAction;
import game.breeze.resource.event.ChangeGridEvent;
import game.breeze.resource.event.ChangeTargetEvent;
import game.breeze.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.TextureDict;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class GameMap extends GameObject {
	private Map<Integer, GameAction> actions = new HashMap<Integer, GameAction>();
	private Map<Integer, GameEvent> events = new HashMap<Integer, GameEvent>();
	private int width;
	private int height;
	private int[][] grid;
	private int rows;
	private int cols;
	private int cellSize;
	public Map<Integer, MapItem> items = new HashMap<Integer, MapItem>();
	public List<Monster> monsters = new ArrayList<Monster>();
	private AStar astar;

	public GameMap(Document doc) {
		Element root = doc.getDocumentElement();
		id = StringUtils.toInteger(root.getAttribute("id"));
		name = root.getAttribute("name");
		width = StringUtils.toInteger(root.getAttribute("width"));
		height = StringUtils.toInteger(root.getAttribute("height"));
		region = new TextureRegion(TextureDict.loadTexture("data/map/res" + id + "/" + root.getAttribute("img")).get(), 0, 0, width, height);

		NodeList event = root.getElementsByTagName("event");
		for (int eventIndex = 0; eventIndex < event.getLength(); eventIndex++) {
			Node item = (Node) event.item(eventIndex);
			EventType type = EventType.valueOf(item.getAttributes().getNamedItem("type").getNodeValue());
			GameEvent ge = parseEvent(type, item);
			ge.setId(StringUtils.toInteger(item.getAttributes().getNamedItem("id").getNodeValue()));
			this.events.put(ge.getId(), ge);
		}

		NodeList actions = root.getElementsByTagName("action");
		for (int actionIndex = 0; actionIndex < actions.getLength(); actionIndex++) {
			Node item = (Node) actions.item(actionIndex);
			ActionType type = ActionType.valueOf(item.getAttributes().getNamedItem("type").getNodeValue());
			GameAction action = null;
			if (type == ActionType.sequence || type == ActionType.parallel) {
				action = (type == ActionType.sequence) ? new SequenceAction() : new ParallelAction();
				String[] subaction = item.getAttributes().getNamedItem("subt").getNodeValue().split(",");
				for (String sa : subaction) {
					ActionType sat = ActionType.valueOf(sa);
					action.addAction(parseAction(sat, item));
				}
			} else {
				action = parseAction(type, item);
			}

			action.setId(StringUtils.toInteger(item.getAttributes().getNamedItem("id").getNodeValue()));

			String[] events = item.getAttributes().getNamedItem("events").getNodeValue().split(",");
			for (String e : events) {
				action.addEvent(this.events.get(StringUtils.toInteger(e)));
			}

			this.actions.put(action.getId(), action);
		}

		NodeList items = root.getElementsByTagName("item");
		for (int itemIndex = 0; itemIndex < items.getLength(); itemIndex++) {
			Node item = items.item(itemIndex);
			MapItemType type = MapItemType.valueOf(item.getAttributes().getNamedItem("type").getNodeValue());
			MapItem mapItem = null;
			switch (type) {
			case npc:
				mapItem = new Npc();
				mapItem.setName(item.getAttributes().getNamedItem("img").getNodeValue());
				mapItem.setWidth(StringUtils.toInteger(item.getAttributes().getNamedItem("width").getNodeValue()));
				mapItem.setHeight(StringUtils.toInteger(item.getAttributes().getNamedItem("height").getNodeValue()));
				Texture npcRes = new Texture(Gdx.files.internal("data/map/res" + id + "/" + mapItem.getName()));// NpcAssets.loadNpc(mapItem.getName());

				List<TextureRegion> frames = new ArrayList<TextureRegion>();

				for (int i = 0; i < 3; i++) {
					frames.add(new TextureRegion(npcRes, i * mapItem.getWidth(), 0, mapItem.getWidth(), mapItem.getHeight()));
				}

				Animation an = new Animation(.15f, 0, frames.toArray(new TextureRegion[frames.size()]));
				((Npc) mapItem).setAni(an);

				frames.clear();
				frames = null;
				break;
			case build:
				mapItem = new Building();
				mapItem.setName(item.getAttributes().getNamedItem("img").getNodeValue());
				mapItem.setWidth(StringUtils.toInteger(item.getAttributes().getNamedItem("width").getNodeValue()));
				mapItem.setHeight(StringUtils.toInteger(item.getAttributes().getNamedItem("height").getNodeValue()));
				Node rotateNode = item.getAttributes().getNamedItem("rotate");
				if (rotateNode != null) {
					mapItem.setRotate(StringUtils.toInteger(rotateNode.getNodeValue()));
				}
				mapItem.setRegion(new TextureRegion(new Texture(Gdx.files.internal("data/map/res" + id + "/" + mapItem.getName())), 0, 0, mapItem.getWidth(), mapItem.getHeight()));
				break;
			case monster:
			default:
				continue;
			}
			mapItem.setMapItemType(type);
			mapItem.setId(StringUtils.toInteger(item.getAttributes().getNamedItem("id").getNodeValue()));
			mapItem.setX(StringUtils.toInteger(item.getAttributes().getNamedItem("x").getNodeValue()));
			mapItem.setY(height - StringUtils.toInteger(item.getAttributes().getNamedItem("y").getNodeValue()) - mapItem.getHeight());
			this.items.put(mapItem.getId(), mapItem);
		}

		Node gridElement = root.getElementsByTagName("grid").item(0);

		NamedNodeMap attr = gridElement.getAttributes();

		for (int i = 0; i < attr.getLength(); i++) {
			String nodeName = attr.item(i).getNodeName();
			if (nodeName.equals("cols")) {
				cols = StringUtils.toInteger(attr.item(i).getNodeValue());
			} else if (nodeName.equals("rows")) {
				rows = StringUtils.toInteger(attr.item(i).getNodeValue());
			} else if (nodeName.equals("size")) {
				cellSize = StringUtils.toInteger(attr.item(i).getNodeValue());
			}
		}

		// NodeList moveableList = gridElement.getChildNodes();
		// grid = new int[rows][cols];
		// int index = 0;
		// for (int row = moveableList.getLength() - 1; row >= 0; row--) {
		// Node node = moveableList.item(row);
		// if (node.getNodeName().equals("moveable")) {
		// grid[index] =
		// StringUtils.toInteger(node.getAttributes().item(0).getNodeValue(),
		// ",");
		// index++;
		// }
		// }
		//
		// astar = new AStar(grid);
	}

	private GameAction parseAction(ActionType type, Node e) {
		GameAction action = null;
		if (type == ActionType.move) {
			MoveAction ma = new MoveAction();
			String[] coordinate = e.getAttributes().getNamedItem(type.name()).getNodeValue().split(",");
			ma.setTo(new Position(StringUtils.toInteger(coordinate[0]), StringUtils.toInteger(coordinate[1])));
			action = ma;
		} else if (type == ActionType.rotate) {
			RotateAction ra = new RotateAction();
			ra.setRotate(StringUtils.toInteger(e.getAttributes().getNamedItem(type.name()).getNodeValue()));
			action = ra;
		}
		return action;
	}

	private GameEvent parseEvent(EventType type, Node e) {
		GameEvent ev = null;
		if (type == EventType.changeGrid) {
			ChangeGridEvent cge = new ChangeGridEvent();
			cge.setFrom(StringUtils.toInteger(e.getAttributes().getNamedItem("from").getNodeValue()));
			cge.setTo(StringUtils.toInteger(e.getAttributes().getNamedItem("to").getNodeValue()));
			List<Position> grids = new ArrayList<Position>();
			cge.setGrids(grids);

			String[] grid = e.getAttributes().getNamedItem("girds").getNodeValue().split(",");
			for (String ps : grid) {
				if (StringUtils.isEmpty(ps)) {
					continue;
				}
				String[] p = ps.split("\\.");
				grids.add(new Position(StringUtils.toInteger(p[1]), StringUtils.toInteger(p[0])));
			}
			ev = cge;
		} else if (type == EventType.changeTarget) {
			ChangeTargetEvent cte = new ChangeTargetEvent();
			cte.setTarget(StringUtils.toInteger(e.getAttributes().getNamedItem("target").getNodeValue()));
			cte.setStatus(StringUtils.toInteger(e.getAttributes().getNamedItem("status").getNodeValue()));
			ev = cte;
		}
		return ev;
	}

	public List<AStarNode> findPath(int x, int y, int dx, int dy) {
		int col = (int) (x / cellSize);
		int row = (int) (y / cellSize);
		int dcol = (int) (dx / cellSize);
		int drow = (int) (dy / cellSize);
		return astar.astar(row, col, drow, dcol);// path.find(x, y,
	}

	/**
	 * @return the width
	 */
	public int getWidth() {
		return width;
	}

	/**
	 * @return the height
	 */
	public int getHeight() {
		return height;
	}

	/**
	 * @return the grid
	 */
	public int[][] getGrid() {
		return grid;
	}

	/**
	 * @return the cellSize
	 */
	public int getCellSize() {
		return cellSize;
	}

	/**
	 * @param width
	 *            the width to set
	 */
	public void setWidth(int width) {
		this.width = width;
	}

	/**
	 * @param height
	 *            the height to set
	 */
	public void setHeight(int height) {
		this.height = height;
	}

	/**
	 * @param grid
	 *            the grid to set
	 */
	public void setGrid(int[][] grid) {
		this.grid = grid;
	}

	/**
	 * @param cellSize
	 *            the cellSize to set
	 */
	public void setCellSize(int cellSize) {
		this.cellSize = cellSize;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see game.breeze.data.GameObject#dispose()
	 */
	@Override
	public void dispose() {
		// TODO Auto-generated method stub
		super.dispose();
		for (GameObject item : items.values()) {
			item.dispose();
		}
		// disposePath();
		astar.dispose();
		items.clear();
		actions.clear();
		events.clear();
		actions = null;
		events = null;
		items = null;
		grid = null;
		astar = null;
	}
}
